/*----------------------------------------------------------------------------------------------
 *
 * This file is XIU's property. It contains XIU's trade secret, proprietary and
 * confidential information.
 *
 * The information and code contained in this file is only for authorized XIU employees
 * to design, create, modify, or review.
 *
 * DO NOT DISTRIBUTE, DO NOT DUPLICATE OR TRANSMIT IN ANY FORM WITHOUT PROPER AUTHORIZATION.
 *
 * If you are not an intended recipient of this file, you must not copy, distribute, modify,
 * or take any action in reliance on it.
 *
 * If you have received this file in error, please immediately notify XIU and
 * permanently delete the original and any copy of any file and any printout thereof.
 * (c) www.xiusdk.cn
 *---------------------------------------------------------------------------------------------*/

#import "CameraViewController.h"
#import "EffectViewController.h"
#import "CanvasView.h"
#import <YNEffectSDK/YNEffectSDK.h>
#import "YNFaceTrack.h"
#import "SettingsViewController.h"

@interface EffectViewController ()<UIScrollViewDelegate>
{
    YNEffectEngine* _engine;
    YNHandle _handle;
    SettingsViewController* _settingsViewController;
    BOOL _settingsViewShown;
    YNBeautyEffect* _beautyEffect;
    YNLandscapeEffect* _landscapeEffect;
    YNSlenderEffect* _slenderEffect;
    
    int _selectedIndex;
    NSMutableArray* _selectedCute;
    __weak IBOutlet UIScrollView *_entriesScrollView;
    NSArray<NSString*>* _entireItems;
    
    NSString* _effectPath;
}
@property (nonatomic) CanvasView *viewCanvas ;

@end

@implementation EffectViewController

-(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if(self)
    {
        _handle = NULL;
        _selectedIndex = -1;
    }
    return self;
}


-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if(self)
    {
        _handle = NULL;
        _selectedIndex = -1;
    }
    return self;
}

-(void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    if(_handle)
    {
        YNFaceTrack_Uninitialize(_handle);
        _handle = NULL;
    }
}

- (void)viewDidLoad {
    
    if( _engine == nil)
    {
        _engine = [YNEffectEngine new];
    }
    
    if( _handle == NULL)
    {
        _handle = YNFaceTrack_Initialize(0);
        NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
        NSString* track_model_path = [resourcePath stringByAppendingPathComponent:@"yn_model_track.tar"];
        YNRESULT ret = YNFaceTrack_LoadModels(_handle, [track_model_path UTF8String]);
        if (ret != YN_OK){
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"错误提示" message:@"算法SDK初始化失败，可能是SDK权限过期，与绑定包名不符" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
            [alert show];
        }
    }
    
    [super viewDidLoad];
    
    [self initVision];
    [self initUI];
    
    [self didSelectItem:0];

}

-(void)setEffectPath:(NSString*)effectPath
{
    _effectPath = effectPath;
}
    
#pragma mark -

- (void)initVision
{
    // remove the view's background color; this allows us not to use the opaque property (self.view.opaque = NO) since we remove the background color drawing altogether
    self.view.backgroundColor = nil;
    
    //stface
    CGFloat factor = CGRectGetHeight(self.view.frame)/1280.0;
    CGRect frame = CGRectMake( 0, 0, 720*factor, 1280*factor ) ;
    self.viewCanvas = [[CanvasView alloc] initWithFrame:frame] ;
    [self.view insertSubview:self.viewCanvas belowSubview:_entriesScrollView];
    self.viewCanvas.center = self.view.center;
    self.viewCanvas.factor = factor;
    self.viewCanvas.backgroundColor = [UIColor clearColor] ;
}

- (void)initUI
{
    CGFloat x = 0;
    NSMutableArray<NSString*>* entireItems = [NSMutableArray array];
    _entireItems = entireItems;
    
    NSString* resPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:_effectPath];
    NSString* defaultEffectPath = nil;
    for( NSString* subPath in [[NSFileManager defaultManager] subpathsAtPath:resPath])
    {
        NSString* fullPath = [resPath stringByAppendingPathComponent:subPath];
        BOOL isDirectory = NO;
        if([[NSFileManager defaultManager] fileExistsAtPath:[fullPath stringByAppendingPathComponent:@"params.json"] isDirectory:&isDirectory] && !isDirectory)
        {
            
            if( [@"original" isEqualToString:subPath] )
            {
                defaultEffectPath = fullPath;
                [entireItems insertObject:fullPath atIndex:0];
            }
            else if( [@"soften" isEqualToString:subPath] )
            {
                [entireItems insertObject:fullPath atIndex:0];
            }
            else if( [@"soften2" isEqualToString:subPath] )
            {
                [entireItems insertObject:fullPath atIndex:0];
            }
            else
            {
                [entireItems addObject:fullPath];
            }
        }
    }
    
    if(defaultEffectPath)
    {
        [entireItems removeObject:defaultEffectPath];
        [entireItems insertObject:defaultEffectPath atIndex:0];
    }
    
    int tag = 0;
    for( NSString* item in entireItems)
    {
        UIImageView* imageView = [[UIImageView alloc] initWithFrame:CGRectMake(x+1, 1, 60-2, _entriesScrollView.bounds.size.height-2)];
        imageView.tag = ++tag;
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        imageView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.2];
        imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfFile:[item stringByAppendingPathComponent:@"thumb.png"] options:0 error:nil]];
    
        UILabel* label = [UILabel new];
        [label setText:[item lastPathComponent]];
        UIFont *font = [UIFont systemFontOfSize:10.0];
        [label setFont:font];
        CGRect bounds = [label textRectForBounds:imageView.frame limitedToNumberOfLines:1];
        label.frame = CGRectMake((imageView.frame.size.width - bounds.size.width)/2, imageView.frame.size.height - bounds.size.height - 1, bounds.size.width, bounds.size.height);
        [imageView addSubview:label];
        
        [_entriesScrollView addSubview:imageView];
    
        x += 60;
    }

    _entriesScrollView.contentSize = CGSizeMake(x, 50);
    
    UITapGestureRecognizer *newTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchOnEntriesView:)];
    [newTapGesture setNumberOfTapsRequired:1];
    [_entriesScrollView addGestureRecognizer:newTapGesture];
}

-(void)touchOnEntriesView:(UIGestureRecognizer*)gestureRecognizer
{
    CGPoint pt = [gestureRecognizer locationInView:_entriesScrollView];
    int index = pt.x / 60;
    if(_entireItems.count > 0 && index < _entireItems.count && index >=0)
    {
        if(index < _entireItems.count)
        {
            [self didSelectItem:index];
        }
    }
}

-(void)didSelectItem:(int)index
{
    if( index < 0 || index >= _entireItems.count || _selectedIndex == index)
        return;
    
    @try
    {
        NSString* item = _entireItems[index];
        NSString* effectPath = [item stringByAppendingPathComponent:@"params.json"];
        
        NSError* error = nil;
        NSData* effectParams = [NSData dataWithContentsOfFile:effectPath options:0 error:&error];
        if( error )
        {
            NSLog(@"read data error - %@", error);
            return;
        }
        NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:effectParams options:0 error:&error];
        if( error )
        {
            NSLog(@"json read error - %@", error);
            return;
        }
        
        _beautyEffect = nil;
        _landscapeEffect = nil;
        _slenderEffect = nil;
        NSMutableArray* effectArray = [NSMutableArray new];
        NSArray* effects = [dict objectForKey:@"effects"];
        for (NSString* effect in effects) {
            if( [effect isEqualToString:@"cute"])
            {
                YNCuteEffect* cuteEffect = [[YNCuteEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"cute.json"]];
                [effectArray addObject:cuteEffect];
            }
            else if( [effect isEqualToString:@"deformation"])
            {
                YNDeformationEffect* deformationEffect = [[YNDeformationEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"deformation.json"]];
                [effectArray addObject:deformationEffect];
            }
            else if( [effect isEqualToString:@"beauty"])
            {
                YNBeautyEffect* beautyEffect = [[YNBeautyEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"beauty.json"]];
                _beautyEffect = beautyEffect;
                [effectArray addObject:beautyEffect];
            }
            else if([effect isEqualToString:@"landscape"])
            {
                YNLandscapeEffect* landscapeEffect = [[YNLandscapeEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"landscape.json"]];
                _landscapeEffect = landscapeEffect;
                [effectArray addObject:landscapeEffect];
            }
            else if([effect isEqualToString:@"slender"])
            {
                YNSlenderEffect* slender = [[YNSlenderEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"slender.json"]];
                [effectArray addObject:slender];
                _slenderEffect = slender;
            }
            else if([effect isEqualToString:@"sharpen"])
            {
                YNSharpenEffect* sharpen = [[YNSharpenEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"sharpen.json"]];
                [effectArray addObject:sharpen];
            }
            else if([effect isEqualToString:@"longleg"])
            {
                YNLongLegEffect* longleg = [[YNLongLegEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"longleg.json"]];
                [effectArray addObject:longleg];
            }
            else if([effect isEqualToString:@"makeup"])
            {
                YNMakeupEffect* makeup = [[YNMakeupEffect alloc] initWithConfig:[item stringByAppendingPathComponent:@"makeup.json"]];
                [effectArray addObject:makeup];
            }
        }
        
        __weak id engine = _engine;
        dispatch_async(_captureSessionQueue, ^{
            [engine addEffects:effectArray clear:YES];
        });
        
        UIImageView* selectedImageView = [_entriesScrollView viewWithTag:index+1];
        selectedImageView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.5];
        
        _selectedCute = nil;
        if( _selectedIndex >= 0 )
        {
            UIImageView* oldImageView = [_entriesScrollView viewWithTag:_selectedIndex+1];
            oldImageView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.2];
        }
        _selectedIndex = index;
        
    }
    @catch(NSException* ex)
    {
        NSLog(@"%@", ex);
    }
}

#pragma mark -
#pragma mark settings
- (IBAction)settingsClicked:(id)sender {
    _settingsViewShown = !_settingsViewShown;
    [self switchSettingView];
}
-(void)touchOnSettingsView:(UIGestureRecognizer*)gestureRecognizer
{
    if(!_settingsViewShown)
        return;
    
    CGPoint pt = [gestureRecognizer locationInView:self.view];
    if( _settingsViewController != nil && _settingsViewController.isViewLoaded)
    {
        if(CGRectContainsPoint(_settingsViewController.view.frame, pt))
            return;
    }
    _settingsViewShown = NO;
    [self switchSettingView];
}

-(void)switchSettingView
{
    if( _settingsViewController == nil)
    {
        _settingsViewController = [[self storyboard] instantiateViewControllerWithIdentifier:@"settingsViewController"];
        [self addChildViewController:_settingsViewController];
        _settingsViewController.captureSession = _captureSession;
        _settingsViewController.view.frame = CGRectMake(0, self.view.bounds.size.height-200, self.view.bounds.size.width, 200);
        _settingsViewController.view.transform = CGAffineTransformMakeTranslation(-_settingsViewController.view.bounds.size.width, 0);
        [self.view insertSubview:_settingsViewController.view aboveSubview:self.viewCanvas];
        
        UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchOnSettingsView:)];
        [tapGesture setNumberOfTapsRequired:1];
        [self.view addGestureRecognizer:tapGesture];

    }
    
    if(_settingsViewShown)
    {
        _settingsViewController.captureSession = _captureSession;
        _settingsViewController.beautyParam = _beautyEffect;
        _settingsViewController.landscapeParam = _landscapeEffect;
        _settingsViewController.slenderParam = _slenderEffect;
        [_settingsViewController reloadData];
        [UIView animateWithDuration:0.2 animations:^{
            self->_settingsViewController.view.transform = CGAffineTransformIdentity;
            self->_entriesScrollView.transform = CGAffineTransformMakeTranslation(0, self->_entriesScrollView.frame.size.height);
        }];
    }
    else
    {
        _settingsViewController.captureSession = nil;
        [UIView animateWithDuration:0.2 animations:^{
            self->_settingsViewController.view.transform = CGAffineTransformMakeTranslation(-self->_settingsViewController.view.bounds.size.width, 0);
            self->_entriesScrollView.transform = CGAffineTransformIdentity;
        }];
    }
}



#pragma mark - Private methods

-(void)setCurrenPersons:(NSArray *)currenPersons
{
    dispatch_async(dispatch_get_main_queue(), ^{
        if( currenPersons.count > 0 )
        {
            [self showFaceLandmarksAndFaceRectWithPersonsArray:currenPersons];
        }
        else
        {
            [self hideFace];
        }
    });
}

- (void) showFaceLandmarksAndFaceRectWithPersonsArray:(NSArray *)arrPersons
{
    if (self.viewCanvas.hidden) {
        self.viewCanvas.hidden = NO ;
    }
    self.viewCanvas.arrPersons = arrPersons ;
    [self.viewCanvas setNeedsDisplay] ;
}

- (void) hideFace {
    if (!self.viewCanvas.hidden) {
        self.viewCanvas.hidden = YES ;
    }
}

#pragma mark - Delegate methods
-(GLuint)processPixelBuffer:(CVPixelBufferRef)pixelBuffer
{
    int iFaceCount = 0;
    YNFacesRef faces = NULL;

    int iRet = YN_OK;
    OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);
    switch (format) {
        case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
        case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
        case kCVPixelFormatType_420YpCbCr8Planar:
        case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
        {
            CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
            unsigned char* pY = (unsigned char*)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
            int width = (int)CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
            int height = (int)CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
            int strides = (int)CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
            iRet = YNFaceTrack_Track(_handle, pY, width, height, strides, YN_PIX_FMT_GRAY8, YN_CLOCKWISE_ROTATE_0, &faces, &iFaceCount);
            CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
            break;
        }
        case kCVPixelFormatType_32RGBA:
        {
            CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
            unsigned char* pRGBA = (unsigned char*)CVPixelBufferGetBaseAddress(pixelBuffer);
            int width = (int)CVPixelBufferGetWidth(pixelBuffer);
            int height = (int)CVPixelBufferGetHeight(pixelBuffer);
            int strides = (int)CVPixelBufferGetBytesPerRow(pixelBuffer);
            iRet = YNFaceTrack_Track(_handle, pRGBA, width, height, strides, YN_PIX_FMT_RGBA8888, YN_CLOCKWISE_ROTATE_0, &faces, &iFaceCount);
            CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
            break;
        }
        case kCVPixelFormatType_32BGRA:
        {
            CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
            unsigned char* pRGBA = (unsigned char*)CVPixelBufferGetBaseAddress(pixelBuffer);
            int width = (int)CVPixelBufferGetWidth(pixelBuffer);
            int height = (int)CVPixelBufferGetHeight(pixelBuffer);
            int strides = (int)CVPixelBufferGetBytesPerRow(pixelBuffer);
            iRet = YNFaceTrack_Track(_handle, pRGBA, width, height, strides, YN_PIX_FMT_BGRA8888, YN_CLOCKWISE_ROTATE_0, &faces, &iFaceCount);
            CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
            break;
        }
        default:
            break;
    }

    if (iRet != YN_OK)
    {
        NSLog(@"failed to track face - %d", iRet);
    }
    else
    {
//        NSMutableArray* faceArray = [NSMutableArray new];
//        for( int i = 0; i < iFaceCount; i++)
//        {
//            YNFacesRef face = faces + i;
//            NSMutableArray* points = [NSMutableArray array];
//            for( int j = 0; j < face->shape.ptsSize; j++)
//            {
//                [points addObject:[NSValue valueWithCGPoint:CGPointMake(face->shape.pts[j].x, face->shape.pts[j].y)]];
//            }
//
//            id outline = [NSValue valueWithCGRect:CGRectMake(face->rect.left, face->rect.top, face->rect.right-face->rect.left, face->rect.bottom-face->rect.top)];
//            [faceArray addObject:@{POINTS_KEY:points,RECT_KEY: outline, RECT_FACEID: @(face->ID)}];
//        }
//        [self setCurrenPersons:faceArray];
    }

    GLuint result = [_engine processVideoFrameWithCVPixelBuffer:pixelBuffer rotation:YN_CLOCKWISE_ROTATE_0 flip:NO faces:faces faceCount:iFaceCount];

    return result;
}


@end
